/*
 * @Description: 网络请求直接返回model
 * @Author: DHL
 * @Date: 2021-12-21 11:45:18
 * @LastEditors: DHL
 * @LastEditTime: 2022-09-19 10:50:39
 */

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'
import qs from 'qs'
import { Convert } from '../convertUtils'
import DuplicateRequest from './duplicate'
import { ContentTypeEnum, MethodEnum, RequestParams } from '/@/constant/http'
import { useAppAuth, useNetworkConfig } from '/@/hooks/setting'
import { Result } from '../../model/baseModel'
import { useLoginUserStore } from '/@/store/modules/loginUser'
import { assign, merge } from 'lodash-es'

/**
 * 是否显示拦截提示消息
 */
let msgBoxShow = true

//////////////////////////////////////////////////
// 配置
/////////////////////////////////////////////////

interface IHttpClientConfig extends AxiosRequestConfig {
  defaultParams?: RequestParams
  /**
   * 是否开启打印
   */
  log?: boolean

  /**
   * 是否开启检查快速点击事件
   */
  checkQuickClick?: boolean

  /**
   * click interval (点击间隔时间)
   */
  clickInterval?: number
}

//////////////////////////////////////////////////
// 网络请求
//////////////////////////////////////////////////

/**
 * 网络请求
 */
class HttpClient {
  public defaultConfig: IHttpClientConfig
  public httpClient: AxiosInstance

  constructor(options: IHttpClientConfig = {}) {

    const config: IHttpClientConfig = {
      baseURL: useNetworkConfig.baseURL,
      checkQuickClick: true,
      timeout: useNetworkConfig.timeout,
      headers: useNetworkConfig.header
    }

    merge(config, options)

    this.httpClient = axios.create(config)
    this.defaultConfig = options

    this.requestInterceptor()

    this.responseInterceptor()
  }


  /**
   * 请求拦截
   */
  private requestInterceptor() {
    this.httpClient.interceptors.request.use(
      function (config) {
        // Do something before request is sent
        return config
      },
      function (error) {
        // Do something with request error
        return Promise.reject(error)
      }
    )
  }

  /**
   * 响应拦截
   */
  private responseInterceptor() {
    this.httpClient.interceptors.response.use(
      function (response) {
        // Do something with response data
        if (response.status >= 200 && response.status < 300) {
          const resultData: Result<any> = response.data

          if (!resultData.success) {
            const code = resultData.code + ''
            switch (code) {
              case 'unauthorized': // 令牌失效
                if (msgBoxShow) {
                  msgBoxShow = false
                  ElMessageBox.confirm('您已被登出，请重新登录', '确定登出', {
                    confirmButtonText: '重新登录',
                    showClose: false,
                    showCancelButton: false,
                    closeOnClickModal: false,
                    closeOnPressEscape: false,
                    type: 'warning'
                  }).then(() => {
                    msgBoxShow = true
                    useLoginUserStore().logOut()
                    location.reload()
                  })
                }
                break

              case 'forbidden': // 无权限访问
                ElMessage({
                  message: resultData.message,
                  type: 'warning'
                })
                break

              case 'system.error': // 异常
                ElMessage.error(resultData.message)
                break

              default:
                break
            }
          }
        }

        return response
      },
      function (error) {
        // Do something with response error

        if (error.response) {
          const req = error.response.request
          const status = error.response.status
          const result = error.response.data

          switch (status) {
            case 401:
              if (msgBoxShow) {
                msgBoxShow = false
                ElMessageBox.confirm('您已被登出，请重新登录', '确定登出', {
                  confirmButtonText: '重新登录',
                  showClose: false,
                  showCancelButton: false,
                  closeOnClickModal: false,
                  closeOnPressEscape: false,
                  type: 'warning'
                }).then(() => {
                  msgBoxShow = true
                  useLoginUserStore().logOut()
                  location.reload()
                })
              }
              break

            case 404:
              ElMessage.error('请求资源不存在：' + req.responseURL)
              break

            case 500:
              ElMessage.error('服务未响应，请稍后再试：' + req.responseURL)
              break

            case 502:
              ElMessage.error('服务未响应，请稍后再试')
              break

            default:
              ElMessage.error(result.message)
          }
        } else {
          ElMessage.error('服务未响应，请稍后重试')
        }

        return Promise.reject(error)
      }
    )
  }

  /**
   * 封装请求类
   * @param path    请求路径
   * @param method  请求方式
   * @param params  参数
   * @param contentType http配置
   * @param optionsSource
   */
  public async request<T>(
    path: string = '',
    method: MethodEnum = MethodEnum.GET,
    params?: RequestParams,
    contentType: ContentTypeEnum = ContentTypeEnum.form,
    optionsSource?: IHttpClientConfig
  ) {
    return new Promise<T>((resolve, reject) => {
      const loginUserStore = useLoginUserStore()

      const options: IHttpClientConfig = merge(this.defaultConfig, optionsSource, {
        headers: {
          'Content-type': contentType,
          'X-Token': loginUserStore.token,
          'X-AppId': useAppAuth.authId,
          'X-AppToken': useAppAuth.authToken,
          'X-AppSecret': useAppAuth.authSecret
        }
      })

      if (loginUserStore.app) {
        assign(options.headers, {
          'X-AppId': loginUserStore.appId,
          'X-AppToken': loginUserStore.appToken,
          'X-AppSecret': loginUserStore.appSecret
        })
      }

      const { headers, clickInterval } = options

      const allParams = Object.assign({}, this.defaultConfig.defaultParams, params)

      const requestConfig: IHttpClientConfig = Object.assign(
        {
          url: `${path}`,
          method,
          headers
        },
        optionsSource
      )

      if (
        options.checkQuickClick === true &&
        DuplicateRequest.hashUrlAndParams(requestConfig.url ?? '', method, allParams, clickInterval)
      ) {
        options.log && console.log('click quick')
        reject()
        return null
      }

      if (method === MethodEnum.GET) {
        requestConfig.params = allParams
      } else {
        if (contentType === ContentTypeEnum.form) {
          requestConfig.data = qs.stringify(allParams)
        } else {
          requestConfig.data = JSON.stringify(allParams)
        }
      }

      this.httpClient
        .request(requestConfig)
        .then((res) => {
          options.log && console.log(res)

          if (res.config.responseType === 'blob') {
            resolve(res.data)
          } else {
            const data: string = JSON.stringify(res.data)
            if (res.status >= 200 && res.status < 300) {
              const rtn = Convert.jsonToModel(data) as T
              resolve(rtn)
            } else {
              reject(data)
            }
          }
        })
        .catch(async (error) => {
          reject(error)
        })
    })
  }
}

//////////////////////////////////////////////////
// 创建https对象
//////////////////////////////////////////////////

export const httpClient = new HttpClient()
